package uy.org.softtech.cssuite.helpers;

import static org.springframework.util.StringUtils.hasText;

import java.util.Locale;
import java.util.regex.Pattern;

import javax.xml.bind.annotation.XmlTransient;

import org.apache.commons.validator.routines.EmailValidator;
import org.apache.commons.validator.routines.UrlValidator;
import org.apache.log4j.Logger;

import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;

/**
 * Clase con métodos utilitarios para validar y trabajar con textos.
 *
 * @author fabian.lobo
 */
@XmlTransient
public class HelperTextos {
	
	public static final Locale URUGUAY = new Locale("es", "UY");
	
	private static Logger log = Logger.getLogger(HelperTextos.class);
	
	private final static String NOMBRE_REGEX 		= "([a-zA-Z áéíóúäëöüàèììù]{2,25}\\s*)+";			//Con determinados letras, entre 2 y 25 caracteres
	private final static String RAZON_SOCIAL_REGEX 	= "([a-zA-Z áéíóúäëöüàèììù,.&()]{2,250}\\s*)+";		//Con determinados letras, entre 2 y 250 caracteres
	private final static String RUT_REGEX 			= "([0-9]{20,25}\\s*)+";							//Sólo números, entre 20 y 25 caracteres 
	
	private final static Pattern NOMBRE_PATRON 			= Pattern.compile(NOMBRE_REGEX, Pattern.CASE_INSENSITIVE);
	private final static Pattern RAZON_SOCIAL_PATRON 	= Pattern.compile(RAZON_SOCIAL_REGEX, Pattern.CASE_INSENSITIVE);
	private final static Pattern RUT_PATRON 			= Pattern.compile(RUT_REGEX, Pattern.CASE_INSENSITIVE);
	
	private static EmailValidator validadorCorreoElectronico = EmailValidator.getInstance();
	private static PhoneNumberUtil validadorTelefono = PhoneNumberUtil.getInstance();
	private static UrlValidator validadorUrl = UrlValidator.getInstance();
	
	/* 
	 * ---------------------------------------
	 * --------    VALIDACIONES    -----------
	 * ---------------------------------------
	 */
	/**
	 * Valida si el texto ingresado cumple con las siguientes reglas:
	 *
	 * <ul>
	 * 		<li>Tener entre 2 y 25 caracteres</li>
	 * 		<li>Tener sólamente estos caracteres (a-zA-Z áéíóúäëöüàèììù)</li>
	 * </ul>
	 * 
	 * @param texto - Texto a validar
	 * @author fabian.lobo
	 */
	public synchronized static boolean validaNombre(String texto) {
		return NOMBRE_PATRON.matcher(texto).matches();
	}
	
	/**
	 * Valida si el texto ingresado es una dirección de correo electrónico válido
	 * 
	 * @param correoElectronico - Dirección de correo electrónico a validar
	 * @author fabian.lobo
	 */
	public synchronized static boolean validaCorreoElectronico(String correoElectronico) {
		return validadorCorreoElectronico.isValid(correoElectronico);
	}
	
	/**
	 * Valida si el texto ingresado es un número de teléfono fijo válido para el país indicado (o UY por defecto).
	 * 
	 * @param telefono - Texto a validar como número telefónico
	 * @param locale - Localización a utilizar, o UY por defecto
	 * @author fabian.lobo
	 * @see PhoneNumberUtil#isValidNumber(PhoneNumber)
	 */
	public synchronized static boolean validaTelefono(String telefono, Locale locale) {
		return validaNumeroTelefonico(telefono, locale, false);
	}
	
	/**
	 * Valida si el texto ingresado es un número de teléfono celular válido para el país indicado (o UY por defecto).
	 * 
	 * @param telefono - Texto a validar como número telefónico celular
	 * @param locale - Localización a utilizar, o UY por defecto
	 * @author fabian.lobo
	 * @see PhoneNumberUtil#isValidNumber(PhoneNumber)
	 */
	public synchronized static boolean validaCelular(String celular, Locale locale) {
		return validaNumeroTelefonico(celular, locale, true);
	}
	
	private synchronized static boolean validaNumeroTelefonico(String telefono, Locale locale, boolean celular) {
		try {
			PhoneNumber numeroTelefono = validadorTelefono.parseAndKeepRawInput(telefono, obtenerPais(locale));
			if(validadorTelefono.isValidNumber(numeroTelefono)) {
				switch (validadorTelefono.getNumberType(numeroTelefono)) {
				case FIXED_LINE:
					return celular ? false : true;
				case MOBILE:
					return celular ? true : false;
				case FIXED_LINE_OR_MOBILE:
					return true;
				default:
					break;
				}
			}
			return false;
		} catch (NumberParseException e) {
			return false;
		}		
	}

	private static String obtenerPais(Locale locale) {
		if(hasText(locale.getCountry())) {
			return locale.getCountry();
		} else {
			if(locale.getLanguage().toLowerCase().equals("en")) {
				return "US";
			}
		}
		return "UY";
	}
	
	/**
	 * Valida si el texto ingresado es una URL válida
	 * 
	 * @param texto - URL a validar
	 * @author fabian.lobo
	 */
	public synchronized static boolean validaUrl(String texto) {
		return validadorUrl.isValid(texto);
	}
	
	/**
	 * Valida si el texto ingresado es una razón social válida
	 * 
	 * @param texto - Razón social a validar
	 * @author fabian.lobo
	 */
	public synchronized static boolean validaRazonSocial(String texto) {
		return RAZON_SOCIAL_PATRON.matcher(texto).matches();
	}
	
	/**
	 * Valida si el texto ingresado es un número de RUT válido
	 * 
	 * @param texto - Número de RUT a validar
	 * @author fabian.lobo
	 */
	public synchronized static boolean validaNumeroRut(String texto) {
		return RUT_PATRON.matcher(texto).matches();
	}
	
	/* 
	 * ---------------------------------------
	 * --------      FORMATEOS     -----------
	 * ---------------------------------------
	 */
	/**
	 * Formatea el número de teléfono dado a formato internacional
	 * 
	 * @param telefono - Número de teléfono
	 * @param locale - Localización usada para determinar el país (y por ende el formato)
	 * @author fabian.lobo
	 */
	public synchronized static String formatearTelefono(String telefono, Locale locale) {
		if(!hasText(telefono)) {
			return telefono;
		}
		String region = obtenerPais(locale);
		try {
			PhoneNumber numeroTelefono = validadorTelefono.parseAndKeepRawInput(telefono, region);
			return validadorTelefono.format(numeroTelefono, PhoneNumberFormat.INTERNATIONAL);
		} catch (NumberParseException e) {
			log.error(e);
			return telefono;
		}
	}
	
	

}
